package org.liu.simpleTank;

import java.awt.*;
import java.util.*;

import org.liu.simpleTank.resource.BulletImage;
import org.liu.simpleTank.resource.TankImage;
import org.liu.simpleTank.resource.TileImage;

/**
 * This class should be created after an empty map is created
 * and before elements are placed onto this map
 * @author Liu Yuhui (185127913@qq.com)
 *
 */
public class Renderer {
	// init layer
	private Layer[] layers;
	{
		layers = new Layer[] {
				new Layer(),
				new Layer(),
				new Layer(),
				new Layer(),
				new Layer()
		};
	}
	
	private Map map;
	public Renderer(Map map) {
		this.map = map;
		// add element added listener 
		map.addAddedListener(new Map.AddedListener() {
			@Override
			public void added(Map.AddEvent e) {
				Element el = e.getElement();
				int idx = typeToLayer[el.type];
				if(idx == -1)
					return;
				layers[idx].add(el);
			}
		});
	}
	
	// coordinate translation
	private static final int TILE_SIZE = 16;
	private static final int SHIFT = 4; // 2 ^ 4 == 16
	private static int colToX(int col) {
		return unitToPixel(col);
	}
	private static int rowToY(int row) {
		return unitToPixel(row);
	}
	private static int unitToPixel(int unit) {
		return unit << SHIFT;
	}
	
	
	// render routines 
	private Graphics g; // updated each render loop
	private int frame = 0;
	public void render(Graphics g) {
		frame++;
		this.g = g;
		g.setColor(Color.black);
		g.fillRect(0, 0, CONSTS.SW, CONSTS.SH);
		for(int i = 0; i < layers.length; i++) {
			Layer layer = layers[i];
			Iterator<Element> iter = layer.iterator();
			while(iter.hasNext()) {
				Element el = iter.next();
				switch(el.type) {
				case CONSTS.NONE:
				case CONSTS.GROUND: break;
				case CONSTS.BRICK: 
				case CONSTS.STEEL: 
				case CONSTS.GRASS: 
				case CONSTS.WATER: this.rTile(el); break;
				case CONSTS.ICE: this.rIce(el); break;
				case CONSTS.TANK_1:
				case CONSTS.TANK_2:
				case CONSTS.TANK_3:
				case CONSTS.TANK_PLAYER1:
				case CONSTS.TANK_PLAYER2: this.rTank(el); break;
				case CONSTS.BULLET: this.rBullet(el); break;
				default:
					break;
				}
			}
		}
	}
	
	private void rEmpty(Element e) {
		
	}
	
	private void rBrick(Element e) {
		rRect(e, Color.red);
	}
	
	private void rSteel(Element e) {
		rRect(e, Color.decode("0xaaaaaa"));
	}
	
	private void rGrass(Element e) {
		rRect(e, Color.green);
	}
	
	private void rWater(Element e) {
		rRect(e, Color.decode("0x7777ff"));
	}
	
	private void rIce(Element e) {
		rRect(e, Color.decode("0xaaaaee"));
	}
	
	private void rTank(Element e) {
		Image img = TankImage.getRobotImg((Tank)e, ((Tank)e).isMoving() ? frame / 15 : 0);
		// robot img is 28 x 28 , but a tank occupies 32 x 32, so add 2 pixel offset
		int offX = colToX(e.col) + 2;
		int offY = rowToY(e.row) + 2;
		double p = ((MovableElement)e).getProgress();
		// progress offset
		if (((MovableElement) e).isMoving()) {
			switch (e.direction) {
			case CONSTS.UP:
				offY += (1 - p) * TILE_SIZE;
				break;
			case CONSTS.RIGHT:
				offX += p * TILE_SIZE;
				break;
			case CONSTS.DOWN:
				offY += p * TILE_SIZE;
				break;
			case CONSTS.LEFT:
				offX += (1 - p) * TILE_SIZE;
				break;
			}
		}
		g.drawImage(img, offX, offY, null);
	}
	
	private void rBullet(Element e) {
		Image img = BulletImage.getBulletImg(e.direction);
		int offX = colToX(e.col);
		int offY = rowToY(e.row);
		double p = ((MovableElement)e).getProgress();
		// vertical
		if(e.direction == CONSTS.UP) {
			offX += 12;
			offY += (1 - p) * 16;
		} else if(e.direction == CONSTS.DOWN) { 
			offX += 12;
			offY += p * 16;
		} else if(e.direction == CONSTS.RIGHT) {// horizontal
			offY += 12;
			offX += p * 16; 
		} else {
			offY += 12;
			offX += (1 - p) * 16; 
		}
		g.drawImage(img, offX, offY, null);
	}
	
	private void rTile(Element e) {
		g.drawImage(TileImage.getTile(e.type, frame / 15), colToX(e.col), rowToY(e.row), null);
	}
	
	// temp render method for test purpose
	private void rRect(Element e, Color c) {
		g.setColor(c);
		g.fillRect(colToX(e.col), rowToY(e.row), unitToPixel(e.w), unitToPixel(e.h));
	}
	
	private static int[] typeToLayer;
	{
		typeToLayer = new int[15];
		typeToLayer[CONSTS.NONE] = -1;
		typeToLayer[CONSTS.GROUND] = 0;
		typeToLayer[CONSTS.BRICK] = 2;
		typeToLayer[CONSTS.STEEL] = 2;
		typeToLayer[CONSTS.GRASS] = 3;
		typeToLayer[CONSTS.WATER] = 0;
		typeToLayer[CONSTS.ICE] = 0;
		typeToLayer[CONSTS.HOME] = 3;
		// movable element types
		typeToLayer[CONSTS.TANK_1] = 2;
		typeToLayer[CONSTS.TANK_2] = 2;
		typeToLayer[CONSTS.TANK_3] = 2;
		typeToLayer[CONSTS.TANK_PLAYER1] = 2;
		typeToLayer[CONSTS.TANK_PLAYER2] = 2;
		typeToLayer[CONSTS.BULLET] = 1;
		typeToLayer[CONSTS.PICKUP] = 4;
		
	}
	
	private static class Layer {
		private LinkedList<Element> data;
		{
			data = new LinkedList<Element>();
		}
		public boolean add(Element el) {
			boolean ret = data.add(el);
			el.addElementListener(new Element.ElementListener() {
				@Override
				public void destroyed(EventObject e) {
					Layer.this.data.remove((Element)e.getSource());
				}
			});
			return ret;
		}
		public boolean remove(Element el) {
			return data.remove(el);
		}
		public Iterator<Element> iterator() {
			return data.iterator();
		}
	}
}
